﻿#include "function.h"
#include <QMessageBox>
#include <QTableView>
#include <ctime>
#include <Windows.h>
#include <QDateTime>
#include "sqlconnectionpool.h"


function::function(QObject *parent) : QObject(parent)
{
}


//*****************Global全局变量******************
int PermissionLevel=2;          //权限等级
//约定返回值如下：0为高级管理员，1为站点管理员，2为普通客户
QString globalUserId;
QString globalUserName;
QString globalPsw;

QString timepath;
QString feepath;
QString path;


//*****************Global全局变量******************

const QString function::login(QString userID, QString pw)
/****************************************************************
 * Function:        function::login
 * Description:         验证用户账号密码是否正确，生成登录信息
 * Calls(被本函数调用的函数):        无
 * Table Accessed(被访问的数据库中的表):  SuperUserTable StationUserTable UserTable
 * Table Updated(被修改的数据库中的表):   无
 * Input:      userID  用户输入的账号   pw 用户输入的密码
 * Output:    测试信息
 * Return:   登录成功与否的QString
 * Others(其他说明，比如测试建议):   测试3种权限的用户登录；测试密码输入错误；测试密码或账号为空；
 *History(历史修改记录)：
 * <作者>   <时间>  <改动>
 * 刘英杰     2021/3        完成1.0版本，并验证了一些测试样例
 * 刘英杰      2021/3/26   在数据库中添加3个账号为139开头的用户，并进行测试;
 *                                      修改BUG：
 *                                      原来的userID.toInt(),因为数据库中user的账号
 *                                      用QString格式，如果userID转换成Int格式，则会出现不可预知的结果;
 *                                      修改BUG：
 *                                       三个表的遍历局部变量混用，已经修改了局部变量的名字
 * ****************************************************************
*/
//登录
{
    //Debug

    qDebug()<<"_______________function::login() is Called____________";
    qDebug() << "login: "<<userID<<pw;

    //为全局变量赋值
    globalUserId=userID;

    globalPsw=pw;

    QSqlDatabase db = SqlConnectionPool::openConnection();


    if(userID!=NULL &&pw!=NULL){

        bool haveThisUser=false;//是否有此用户


        //    QSqlQuery ss(db);
        //     bool anss = ss.exec(QString("delete from GoodsTable where goodsId='34'"));
        //    if(anss){
        //        qDebug() << "删除成功****************************************************";

        //    }
        //    else{
        //        qDebug() << "删除失败****************************************************";
        //    }

        //***********1.查询高级管理员*************************************************************************
        QSqlQuery result = db.exec(" select * from SuperUserTable");


        QString userIdValue;
        QString pswValue;
        QString nameValue;

        //        qDebug()<<"---Searching in SuperUserTable";
        while(result.next()){
            //            qDebug()<<"GOT a Record in SuperUserTable";
            userIdValue=result.value("userID").toString();
            nameValue=result.value("_name").toString();
            pswValue=result.value("_password").toString();
            //            qDebug()<<"1.userID:"<<userIdValue<<"2.nameValue:"<<nameValue<<"3.pswValue:"<<pswValue;

            if(userID==userIdValue){

                //找到了匹配的用户id

                if(pw.compare(pswValue)==0){
                    //密码匹配
                    globalUserName=nameValue;
                    qDebug()<<"成功登录，该用户信息如下";
                    qDebug()<<"user_id:"<<userIdValue;
                    qDebug()<<"name:"<<nameValue;
                    qDebug()<<"password:"<<pswValue;
                    qDebug()<<"权限等级："<<0;
                    PermissionLevel=0;
                    qDebug()<<"_______________Return from  function::login()____________";
                    return("Success");
                    SqlConnectionPool::closeConnection(db);

                }else{
                    //密码不匹配
                    qDebug()<<"_______________Return from  function::login()____________";
                    return("密码不正确");
                }

                haveThisUser=true;

            }/*if(userID.toInt()==userIdValue)*/

        }/*while(result.next())*/



        //***********2.查询站点管理员*************************************************************************
        QSqlQuery result2 = db.exec(" select * from StationUserTable");


        QString userIdValue2;
        QString pswValue2;
        QString nameValue2;

        //        qDebug()<<"---Searching in StationUserTable";
        while(result2.next()){
            //            qDebug()<<"GOT a Record in StationUserTable";
            userIdValue2=result2.value("userID").toString();
            nameValue2=result2.value("_name").toString();
            pswValue2=result2.value("_password").toString();
            //            qDebug()<<"1.userID:"<<userIdValue2<<"2.nameValue:"<<nameValue2<<"3.pswValue:"<<pswValue2;

            if(userID==userIdValue2){

                //找到了匹配的用户id

                if(pw.compare(pswValue2)==0){
                    //密码匹配
                    globalUserName=nameValue2;
                    qDebug()<<"成功登录，该用户信息如下";
                    qDebug()<<"user_id:"<<userIdValue2;
                    qDebug()<<"name:"<<nameValue2;
                    qDebug()<<"password:"<<pswValue2;
                    qDebug()<<"权限等级："<<1;
                    PermissionLevel=1;
                    qDebug()<<"_______________Return from  function::login()____________";
                    SqlConnectionPool::closeConnection(db);
                    return("Success");

                }else{
                    //密码不匹配
                    qDebug()<<"_______________Return from  function::login()____________";
                    return("密码不正确");
                }

                haveThisUser=true;

            }/*if(userID.toInt()==userIdValue)*/

        }/*while(result.next())*/
        //***********3.查询普通用户*************************************************************************
        QSqlQuery result3 = db.exec(" select * from UserTable");


        QString userIdValue3;
        QString pswValue3;
        QString nameValue3;

        //        qDebug()<<"---Searching in UserTable";
        while(result3.next()){

            //            qDebug()<<"GOT a Record in UserTable";
            userIdValue3=result3.value("userID").toString();
            nameValue3=result3.value("_name").toString();
            pswValue3=result3.value("_password").toString();
            //            qDebug()<<"1.userID:"<<userIdValue3<<"2.nameValue:"<<nameValue3<<"3.pswValue:"<<pswValue3;

            if(userID==userIdValue3){

                //找到了匹配的用户id

                if(pw.compare(pswValue3)==0){
                    //密码匹配
                    globalUserName=nameValue3;
                    qDebug()<<"成功登录，该用户信息如下";
                    qDebug()<<"user_id:"<<userIdValue3;
                    qDebug()<<"name:"<<nameValue3;
                    qDebug()<<"password:"<<pswValue3;
                    qDebug()<<"权限等级："<<2;
                    PermissionLevel=2;
                    qDebug()<<"_______________Return from  function::login()____________";
                    SqlConnectionPool::closeConnection(db);
                    return("Success");

                }else{
                    //密码不匹配
                    qDebug()<<"_______________Return from  function::login()____________";
                    return("密码不正确");
                }

                haveThisUser=true;

            }/*if(userID.toInt()==userIdValue)*/

        }/*while(result.next())*/

        if(!haveThisUser){
            qDebug()<<"_______________Return from  function::login()____________";
            return("没有此用户");
        }


    }else{
        qDebug()<<"_______________Return from  function::login()____________";
        return("账号和密码不能为空");
    }/*if(userID!=NULL &&pw!=NULL)*/

    qDebug()<<"_______________Return from  function::login()____________";
    return("异常");


}





const QString function::registering(QString userID, QString pw, QString name,
                                    int permission, QString siteResponse, QString RecommendationCode)
/****************************************************************
 * Function:        function::registering
 * Description:         验证账号是否合法，如果是站点管理员注册的话，注册站点是否为已有站点
 * Calls(被本函数调用的函数):
 * Table Accessed(被访问的数据库中的表):  SuperUserTable StationUserTable UserTable StationTable
 * Table Updated(被修改的数据库中的表):    SuperUserTable StationUserTable UserTable StationTable
 * Input:      userID  用户输入的账号   需要检测合法性
 *                  pw 用户输入的密码   不能为空
 *                  name 用户姓名  不能为空
 *                  permission   用户等级 0为高级管理员，1为站点管理员，2为普通客户
 *                  siteResponse 所管理站点（可缺省） 站点必须为已有站点
 * Output:    一些Debug用的信息
 * Return:   注册成功与否的QString
 * Others(其他说明，比如测试建议):
 *      黑盒测试时需要测试用户可能出现的各种输入错误：账号、密码、用户名之一为空，账号与已有账号重复，
 *所填站点非已存在站点。白盒测试时也要考虑需要精细到哪种覆盖，比如至少要做到白盒测试时的语句覆盖。
 *      1.注册账号合法性：   0.账号不能为空
 *                                      1.账号重复：账号与三个表查重；
 *                                      2.账号格式不对：应该用手机号注册，这里假设电话号码的要求是：以1开头，第2位是3，5，8，总共11位
 *                                      3.账号中有数字之外的字符（如何检测）
 *                                      4.账号不应该有前缀和后缀空格
 *      2.当用户的输入都正确时，应该在对应的表中添加数据（三种用户表和一个站点管理表）
 *      3.注意，注册成功后不应该修改代表用户账号和用户权限等级的全局变量，因为上述全局变量是在用户处于登录状态时才进行更新的。
 *History(历史修改记录)：
 * <作者>   <时间>  <改动>
 * 刘英杰     2021/3/30   完成1.0版本，测试了几种输入错误，并且成功模拟3种用户的注册
 * 刘英杰  2021/3/30   补充了判断密码和用户名是否长于20（数据库中最多存这么长，再长会被截断）
 * ****************************************************************
 * 李云帆  2021.4.14   增加RecommendationCode变量，用来传递注册页的推荐码
 * 李春毅  2021.4.48   增加密码长度限制，增加了对推荐码的检验，检验规则：输入6-10位全为数字的字符串，该串后6位的1、3、5位需为偶数，2、4、6位需为奇数：214365即可
*/
//注册
{
    qDebug()<<"_______________function::registering() is Called____________";
    qDebug() << "input: "<<userID<<pw<<name<<permission<<siteResponse;

    //检查账号的格式是否正确
    if(RecommendationCode.length() < 6 || RecommendationCode.length() > 10){

        if(permission==0)
            return("推荐码错误");
    }
    long code = RecommendationCode.toLong();
    qDebug() << "Input RecommendationCode:" <<  code;
    if(!      (code / 100000 % 2 == 0
               && code % 100000 / 10000 % 2 == !0
               && code % 10000 / 1000  % 2 == 0
               && code % 1000 / 100  % 2 == !0
               && code % 100 / 10  % 2 == 0
               && code % 10 % 2 != 0 )){
            if(permission==0)
                return("推荐码错误");
    }
    if(userID==NULL){

        return("账号不能为空");

    }
    if(pw.length() > 10){
        return ("密码长度超过10位");
    }
    if(pw.length() < 6){
        return ("密码长度少于6位");
    }

    //检查密码格式是否正确
    if(pw==NULL){

        return("密码不能为空");

    }else if(pw.length()>20){

        return("密码不能多于20个字符");

    }
    //检查用户姓名是否正确
    if(name==NULL){

        return("用户名不能为空");

    }else if(name.length()>20){

        return("用户名不能多于20个字符");

    }

    //这里假设电话号码的要求是：以1开头，第2位是3，5，8，总共11位（不考虑前缀和后缀空格）

    std::regex r("^1(3|5|8)\\d{9}$");

    std::string userIdString = userID.toStdString();


    bool bValid=std::regex_match(userIdString,r);

    if(!bValid){
        qDebug()<<"_______________Return from  function::registering()____________";
        return("账号应为手机号");
    }else{
        qDebug()<<"账号符合手机号格式："<<userID;
    }

    //检查是否与已有账号重复
    QSqlDatabase db = SqlConnectionPool::openConnection();

    bool isRedundant=false;

    //***********1.查询高级管理员*************************************************************************
    QSqlQuery result = db.exec(" select * from SuperUserTable");
    QString userIdValue;
    QString pswValue;
    QString nameValue;

    while(result.next()){
        userIdValue=result.value("userID").toString();
        nameValue=result.value("_name").toString();
        pswValue=result.value("_password").toString();

        if(userID==userIdValue){

            isRedundant=true;

        }/*if(userID.toInt()==userIdValue)*/

    }/*while(result.next())*/



    //***********2.查询站点管理员*************************************************************************
    QSqlQuery result2 = db.exec(" select * from StationUserTable");
    QString userIdValue2;
    QString pswValue2;
    QString nameValue2;


    while(result2.next()){

        userIdValue2=result2.value("userID").toString();
        nameValue2=result2.value("_name").toString();
        pswValue2=result2.value("_password").toString();


        if(userID==userIdValue2){

            isRedundant=true;

        }/*if(userID.toInt()==userIdValue)*/

    }/*while(result.next())*/
    //***********3.查询普通用户*************************************************************************
    QSqlQuery result3 = db.exec(" select * from UserTable");
    QString userIdValue3;
    QString pswValue3;
    QString nameValue3;


    while(result3.next()){
        userIdValue3=result3.value("userID").toString();
        nameValue3=result3.value("_name").toString();
        pswValue3=result3.value("_password").toString();

        if(userID==userIdValue3){

            isRedundant=true;

        }/*if(userID.toInt()==userIdValue)*/

    }/*while(result.next())*/
    if(isRedundant){
        qDebug()<<"_______________Return from  function::login()____________";
        SqlConnectionPool::closeConnection(db);
        return("此账号已注册");
    }

    //检查权限和站点，如果是站点管理员则检查站点是否为存在的站点

    //定义变量保存查找出来的站点信息，方便之后向StationTable插入新的记录
    QString userSiteId;
    double  usercoordinateX;
    double usercoordinateY;
    int userSiteClass;
    if(permission==1){

        QSqlQuery result = db.exec(" select * from StationTable");
        QString siteId;
        double coordinateX;
        double coordinateY;
        QString responsibleId;
        int siteClass;

        bool haveThisSite=false;
        while(result.next()){
            siteId=result.value("siteId").toString();
            coordinateX=result.value("coordinateX").toDouble();
            coordinateY=result.value("coordinateY").toDouble();
            responsibleId=result.value("responsibleId").toString();
            siteClass=result.value("siteClass").toInt();

            if(siteResponse==siteId){

                haveThisSite=true;
                //将用户所管理的站点信息存储下来
                userSiteId=siteId;
                usercoordinateX=coordinateX;
                usercoordinateY=coordinateY;
                userSiteClass=userSiteClass;

            }

        }

        if(!haveThisSite){
            qDebug()<<"_______________Return from  function::login()____________";
            SqlConnectionPool::closeConnection(db);
            return("此用户管理的站点不存在");
        }
    }

    qDebug()<<"注册成功";
    if(permission==1){
        qDebug()<<"账号 "<<userID<<"密码"<<pw<<"用户名"<<name<<"权限等级"<<permission<<"管理站点"<<siteResponse;
    }else{
        qDebug()<<"账号 "<<userID<<"密码"<<pw<<"用户名"<<name<<"权限等级"<<permission;
    }

    //数据库写入

    QSqlQuery qsQuery =QSqlQuery(db);
    if(permission==0){

        QString strSqlText("insert into SuperUserTable (userId,_name,_password)\
                           values (:userId,:_name,:_password)");
                           qsQuery.prepare(strSqlText);
                qsQuery.bindValue(":userId",userID);
        qsQuery.bindValue(":_name",name);
        qsQuery.bindValue(":_password",pw);
        result.clear();
        bool result_successed =qsQuery.exec();
        if(result_successed){

            qDebug()<<"插入用户信息成功";

        }else{

            qDebug()<<"插入用户信息失败！";

        }
    }else  if(permission==1){

        QString strSqlText("insert into StationUserTable (userId,_name,_password,siteId)\
                           values (:userId,:_name,:_password,:siteId)");
                           qsQuery.prepare(strSqlText);
                qsQuery.bindValue(":userId",userID);
        qsQuery.bindValue(":_name",name);
        qsQuery.bindValue(":_password",pw);
        qsQuery.bindValue(":siteId",userSiteId);
        result.clear();
        bool result_successed =qsQuery.exec();
        if(result_successed){

            qDebug()<<"插入用户信息成功";

        }else{

            qDebug()<<"插入用户信息失败！";

        }
        QString strSqlText2("insert into StationTable (siteId,coordinateX,coordinateY,responsibleId,siteClass)\
                            values (:siteId,:coordinateX,:coordinateY,:responsibleId,:siteClass)");
                            qsQuery.prepare(strSqlText2);
                qsQuery.bindValue(":siteId",userSiteId);
        qsQuery.bindValue(":coordinateX",usercoordinateX);
        qsQuery.bindValue(":coordinateY",usercoordinateY);
        qsQuery.bindValue(":responsibleId",userID);
        qsQuery.bindValue(":siteClass",userSiteClass);
        result.clear();
        bool result_successed2 =qsQuery.exec();
        if(result_successed2){

            qDebug()<<"插入新的站点信息(新的负责人ID)成功";

        }else{

            qDebug()<<"插入新的站点信息(新的负责人ID)失败！";

        }
    }else  if(permission==2){

        QString strSqlText("insert into UserTable (userId,_name,_password)\
                           values (:userId,:_name,:_password)");
                           qsQuery.prepare(strSqlText);
                qsQuery.bindValue(":userId",userID);
        qsQuery.bindValue(":_name",name);
        qsQuery.bindValue(":_password",pw);
        result.clear();
        bool result_successed =qsQuery.exec();
        if(result_successed){

            qDebug()<<"插入用户信息成功";

        }else{

            qDebug()<<"插入用户信息失败！";

        }
    }


    qDebug()<<"_______________Return from  function::registering()____________";
    SqlConnectionPool::closeConnection(db);
    return("Success");

}

const int function::getResponse(int userID)
/****************************************************************
 * fun:获取函数
 * 刘英杰
 * 测试完毕
 * 测试样例
 *
 * ****************************************************************
 * 获取权限等级信息
 */
//获取权限等级信息
{
    return PermissionLevel;
}


const QString function::getPersonalInfo(int userID)
/*得到个人信息
* Function:                             function::getPersonalInfo
* Description:                          得到个人账户信息
* Calls(被本函数调用的函数):               无
* Table Accessed(被访问的数据库中的表):    SuperUserTable StationUserTable UserTable
* Table Updated(被修改的数据库中的表):     无
* Input:           globalUserId         用户账号
*                  globalUserName       用户名
*                  PermissionLevel      用户权限等级
* Output:                               用户帐号、用户名、用户权限等级
* Return:                               成功则返回包含帐号、用户名、用户权限等级的字符串
*                                       失败则返回失败原因
* Others(其他说明，比如测试建议):
*                                       问题：前端无法第一时间显示
*
* History(历史修改记录)：
* <作者>      <时间>         <改动>
* 李春毅     2021/3/30      完成1.0版本
* 刘英杰   2021/4/11       修改站点查询BUG
*/
{
    QSqlDatabase db = SqlConnectionPool::openConnection();

    QString whichTableToFind;
    QString power;  //权限字符化
    switch(PermissionLevel){
    case 0: whichTableToFind = "UserTable";
        power = "超级管理员\n管理全站";
        break;
    case 1: whichTableToFind = "StationUserTable";
        power = "站点管理员\n管理站点:     ";
        break;
    case 2: whichTableToFind = "SuperUserTable";
        power = "客户";
        break;
    }

    qDebug() << "gettingPersonalInfo: " + globalUserId;;

    QString resSite = "";
    if(PermissionLevel == 1){
        QSqlQuery result = db.exec(" select * from StationTable");

        while(result.next()){


            if(userID==result.value("responsibleId").toInt()){
                resSite=result.value("siteId").toString();

            }

        }

    }
    QString userInfo ="用户帐号:     " + globalUserId +"\n用户名:       "+ globalUserName + "\n用户权限等级: " + power + resSite + "\n";
    qDebug()<<userInfo;
    qDebug() << globalUserId << globalUserName;
    SqlConnectionPool::closeConnection(db);
    return userInfo; //id +　姓名　＋　权限　＋　管理权限 + 权限范围
}

const QString function::setNewPW(int userID, QString oldPW, QString newPW)
//设置新密码
/*
* Function:                             function::setNewPW
* Description:                          设置新密码
* Calls(被本函数调用的函数):               无
* Table Accessed(被访问的数据库中的表):    SuperUserTable StationUserTable UserTable
* Table Updated(被修改的数据库中的表):     SuperUserTable StationUserTable UserTable
* Input:           globalUserId         用户账号
*                  PermissionLevel      用户权限等级
* Output:                               修改密码的结果
* Return:                               成功则返回Success
*                                       失败则返回失败原因
* Others(其他说明，比如测试建议):
*
* History(历史修改记录)：
* <作者>      <时间>         <改动>
* 李春毅     2021/3/25      完成1.0版本，update无法使用，无法修改密码
* 李春毅     2021/3/30      完成2.0版本，update语句成功实现，可以修改密码
*/

{

    qDebug() << "settingNewPW: "<<userID<<oldPW<<newPW;

    QSqlDatabase db = SqlConnectionPool::openConnection();

    QString whichTableToFind;  //定位到不同权限的表

    switch(PermissionLevel){
    case 0: whichTableToFind = "SuperUserTable";
        break;
    case 1: whichTableToFind = "StationUserTable";
        break;
    case 2: whichTableToFind = "UserTable";
        break;
    }

    if(oldPW != globalPsw){
        return "原密码输入错误";
    }

    else if(oldPW == newPW){
        return "新密码与原密码相同";
    }

    else if(newPW.length() > 10){
        return "密码长度超过10位";
    }

    else if(newPW.length() < 6){
        return "密码长度少于6位";
    }
    else{

        QSqlQuery query(db);

        query.prepare(QString("UPDATE " + whichTableToFind + " SET _password='" + newPW + "' WHERE userId=" + globalUserId));


        bool res = query.exec();
        if(res){
            qDebug()<<"update password succeed";
            globalPsw = newPW;
        }
        else{
            qDebug()<<"update password failed";
            return "数据库出现错误";
        }

    }
    SqlConnectionPool::closeConnection(db);
    return("Success");
}

const QString function::setNewName(int userID, QString newName)
//设置新昵称
/*
* Function:                             function::setNewPW
* Description:                          设置新密码
* Calls(被本函数调用的函数):               无
* Table Accessed(被访问的数据库中的表):    SuperUserTable StationUserTable UserTable
* Table Updated(被修改的数据库中的表):     SuperUserTable StationUserTable UserTable
* Input:           globalUserId         用户账号
*                  PermissionLevel      用户权限等级
*                  newName              新昵称
* Output:                               修改昵称的结果
* Return:                               成功则返回Success
*                                       失败则返回失败原因
* Others(其他说明，比如测试建议):
*
* History(历史修改记录)：
* <作者>            <时间>         <改动>
* 李春毅           2021/3/25      完成1.0版本，update无法使用，无法修改昵称
* 李春毅,刘英杰     2021/3/30      完成2.0版本，update语句成功实现，可以修改昵称
* 李春毅          2021.4.18        修改返回字符串“名字” 为“昵称”
*/
{qDebug() << "settingNewName: "<<userID <<"wants"<<newName;

    QSqlDatabase db = SqlConnectionPool::openConnection();

    QString whichTableToFind;
    switch(PermissionLevel){
    case 0: whichTableToFind = "SuperUserTable";
        break;
    case 1: whichTableToFind = "StationUserTable";
        break;
    case 2: whichTableToFind = "UserTable";
        break;
    }
    if(newName.length() > 20){
        return "昵称长度超过20位";
    }
    else if(newName.length() <= 0){
        return "昵称为空";
    }
    else{
        QSqlQuery query(db);
        bool res = query.exec(QString("UPDATE    "+ whichTableToFind + "   SET   _name='" + newName + "'    where userId=" + globalUserId));
        SqlConnectionPool::closeConnection(db);

        if(res){
            qDebug() << "修改昵称成功";
            globalUserName = newName;
        }
        else{

            return "数据库出现错误";
            qDebug() << "修改昵称失败";
        }
    }


    return("Success");
}
const QString function::newSite(QString siteName, QString x, QString y,
                                int type)
/****************************************************************
 * Function:        function::newSite
 * Description:         增加站点
 * Calls(被本函数调用的函数):  strai_dist
 * Table Accessed(被访问的数据库中的表):  StationTable
 * Table Updated(被修改的数据库中的表):   StationTable
 * Input:      siteName  新增站点名称
 *             x 新增站点x坐标
 *             y 新增站点y坐标
 *             type 新增站点类型
 * Output:    一些Debug用的信息
 * Return:   注册成功与否的QString
 * Others(其他说明，比如测试建议):
 * 测试： 1、站点名称不能和已有站点名称重复
 *       2、站点名称应满足中转站点和驿站各自的命名规范
 *       3、根据条件判断，当周围相同类型的站点较多时，不应添加站点
 *History(历史修改记录)：
 * <作者>   <时间>  <改动>
 * 金雨展     2021/3/20   完成1.0版本，测试了几种输入错误
 *           2021/4/18    修改BUG
 * ****************************************************************
*/
//设置新站点
{
    //请注意，坐标变量是用户的直接输入内容（QString），需要进行合法性判断
    //请注意，类型type有0和1两个值，分别代表中继站点、中转站点

    /***************************数据库***************************/
    QSqlDatabase db = SqlConnectionPool::openConnection();

    /*********************************/
    RoutePlanning rp;

    qDebug()<<"*************************************************************";
    //*****************检查站点名称是否合适********************************
    if(siteName.length()>1 && type == 0){
        qDebug()<<"站点名称不合规范";
        return("站点名称不合规范");
    }
    QChar a = siteName.at(0);
    int num = siteName.toInt();
    qDebug()<< a;
    if(type == 0 && ( a <'A' || a > 'Z')){
        qDebug()<<"*************************************************************";

        qDebug()<<"站点名称不合规范";
        return("站点名称不合规范");
    }
    else if(type == 1 && ((num < 00 || num > 99) || siteName.length() != 2)){
        qDebug()<<"站点名称不合规范";
        return("站点名称不合规范");
    }
    qDebug()<<"******************城市名字没有问题****************";
    QString rowname = "siteId,siteClass";
    QString tablename = "StationTable";
    QSqlQuery result = db.exec(" select " + rowname + " from " + tablename + ";");
    qDebug()<<"******************读城市开始****************";
    SqlConnectionPool::closeConnection(db);
    int m = 0;
    while(result.next()){
        qDebug()<<"siteId:"<<result.value("siteId");
        if(type !=result.value("siteClass").toInt() ){
            rp.city<<result.value("siteId").toString();
            m++;
            qDebug()<<"&&&";
            qDebug()<<m;
            if(result.value("siteId").toString().compare(siteName)==0){
                qDebug()<<"该站点名称已使用";
                return("该站点名称已使用");
            }
        }
        qDebug()<<result.value("siteId");
        qDebug()<<"***";
        qDebug()<<result.value("siteClass").toInt();
        qDebug()<<"***";

    }

    qDebug()<<"******************读城市结束****************";

    //int n = rp.readDist();

    double distance = 0;
    double x1 = x.toDouble();
    double y1 = y.toDouble();
    qDebug()<<"*************开始检测位置是否合适**************";
    qDebug()<<m;
    for(int i = 0;i < m ; ++i){
        distance = rp.strai_dist(rp.city.at(i),x1,y1);
        if(distance < 4){
            qDebug()<<"该位置不适合插入站点";
            qDebug()<<distance;
            return("附近有其他站点，该位置不适合插入新站点");
        }
        else{
            qDebug()<<rp.city.at(i);
            qDebug()<<distance;
            qDebug()<<"该位置适合插入站点";

        }
    }

    qDebug()<<"*************检测位置是否合适完毕**************";
    //ODBC插入数据
    QSqlQuery query(db);
    //db.exec(QString("delete from StationTable where responsibleId='003' and siteId='A'" ));
    // query.prepare(QString(("insert into StationTable(siteId,coordinateX,coordinateY,responsibleId,siteClass)values('"+siteName+"',14.2,13.2,'002','" + type +"')" )));
    //   "('" + siteName+ "','" + x1 + "','" + y1 +  "','13900000001','" + type +"')"  )

    if(type == 0)
        type = 1;
    else if(type == 1)
        type = 0;
    QSqlQuery qsQuery(db);
    QString strSqlText("insert into StationTable(siteId,coordinateX,coordinateY,responsibleId,siteClass)\
                       values (:siteId,:coordinateX,:coordinateY,:responsibleId,:siteClass)");
                       qsQuery.prepare(strSqlText);
            qsQuery.bindValue(":siteId",siteName);
    qsQuery.bindValue(":coordinateX",x1);
    qsQuery.bindValue(":coordinateY",y1);
    qsQuery.bindValue(":siteClass",type);
    qsQuery.bindValue(":responsibleId","13900000001");
    //result.clear();
    bool result_successed = qsQuery.exec();
    SqlConnectionPool::closeConnection(db);
    if(result_successed){
        qDebug()<<"插入成功！";
        return("Success");

    }else {
        qDebug()<<"插入失败！";
        return("插入失败！");
    }


}

const QStringList function::getTerminalList()
//得到 终端站点的列表
/*
* function:                             function::getSitesList
* Description:                          得到终端站点的列表
* Calls(被本函数调用的函数):               getSitesList
* Output:                               终端站点名称
* Return:                               成功则返回终端站点列表
*                                       失败则无返回
* Others(其他说明，比如测试建议):
*
* History(历史修改记录)：
* <作者>      <时间>         <改动>
* 李云帆     2021/4/19       创建
*/
{
    QStringList strList = getSitesList();
    QStringList terminalList;
    for(auto item : strList)
        if(!item.contains(QRegExp("^\\d+$")))
                terminalList.append(item);

    return terminalList;
}

const QStringList function::getSitesList()
//得到 所有站点的列表
/*
* Function:                             function::getSitesList
* Description:                          得到所有站点的列表
* Calls(被本函数调用的函数):               无
* Table Accessed(被访问的数据库中的表):     StationTable
* Table Updated(被修改的数据库中的表):
* Input:
* Output:                               所有站点名称
* Return:                               成功则返回站点列表
*                                       失败则无返回
* Others(其他说明，比如测试建议):
*
* History(历史修改记录)：
* <作者>      <时间>         <改动>
* 李春毅     2021/3/30      完成1.0版本，成功实现所需功能
*/
{
    qDebug() << "gettingSitesList...";

    QStringList strList;
    //******************************数据库连接*********************************
    QSqlDatabase db = SqlConnectionPool::openConnection();
    //****************************数据库连接***********************************

    QSqlQuery result = db.exec("select siteId from StationTable");

    while(result.next()){
        if(!strList.contains(result.value("siteId").toString()))
            strList << result.value("siteId").toString();
    }
    SqlConnectionPool::closeConnection(db);
    return(strList);
}

const QString function::delSite(QString siteName)
//删除某个站点
/*
* Function:                             function::delSite
* Description:                          先判断该站点有没有运单即将到达，再删除站点，并删除该站点相关的路线（可选）
* Calls(被本函数调用的函数):               无
* Table Accessed(被访问的数据库中的表):    StationTable
* Table Updated(被修改的数据库中的表):     StationTable
* Input:                                siteName 站点Id
* Output:                               删除站点Id
* Return:                               成功则成功
*                                       失败则返回原因
* Others(其他说明，比如测试建议):           如果一个站点还有运单未到，那么就不能删除该站点
*
* History(历史修改记录)：
* <作者>      <时间>         <改动>
* 李春毅     2021/3/30      完成1.0版本
* 李春毅     2021/4/18      修改无法删除站点的bug，级联删除了路线
*/
{
    //******************************数据库连接*********************************
    QSqlDatabase db = SqlConnectionPool::openConnection();
    //****************************数据库连接***********************************
    QString errReason = "";
    /*
         *数据库删除siteName站点的信息
         *同时删除路线表里和siteName站点相关的所有路线
         *如果失败则用qDebug()打印原因、并把原因赋值给 errReason;
         */
    //    QString
    QSqlQuery result2 = db.exec(QString("select * from GoodsTable"));
    while(result2.next()){
        if(result2.value("trackingNum").toString().contains(siteName)){

            if(result2.value("trackingNum").toString().indexOf(result2.value("currentPlace").toString()) <= result2.value("trackingNum").toString().indexOf(siteName)){
                errReason = "该站点有即将到达的运单，无法删除！";
                return errReason;
            }

        }
    }


    QSqlQuery query = QSqlQuery(db);

    QString strSqlText("DELETE FROM StationTable WHERE siteId = :siteId ");

    query.prepare(strSqlText);

    query.bindValue(":siteId", siteName);


    bool ans = query.exec();
    SqlConnectionPool::closeConnection(db);
    if(ans){
        qDebug() << "删除站点成功:" + siteName;

        query.clear();
        bool ans2 = query.exec(QString("DELETE FROM LineTable WHERE siteId='" + siteName + "'")); //出发路线

        query.clear();
        bool ans3 = ans2 && (bool)query.exec(QString("DELETE FROM LineTable WHERE toSite='" + siteName + "'")); //到站路线

        if(ans3){
            qDebug() << "删除站点相关路线成功";
        }
        else{
            qDebug() << "未删除相关路线，请手动删除路线";
            errReason = "未删除相关路线，请手动删除路线";
        }
    }
    else{
        errReason = "删除失败";
        qDebug() << "删除失败";
    }

    qDebug() << "deletingSite: " << siteName;
    //...
    if(errReason == "")
        return("Success");
    return(errReason);
}

const QString function::newWay(QString from, QString to,
                               QString time, QString fee)
/****************************************************************
 * Function:       function::newWay
 * Description:         建立新路线
 * Calls(被本函数调用的函数):
 * Table Accessed(被访问的数据库中的表):StationTable LineTable
 * Table Updated(被修改的数据库中的表):LineTable
 * Input:      QString from   始发站, QString to  终点站, QString time  时间, QString fee   费用
 * Output:    调试信息
 * Return:   表示建立路线成功与否的字符串
 * Others(其他说明，比如测试建议):
 *History(历史修改记录)：
 * <作者>   <时间>  <改动>
*刘英杰    2021/3     完成1.0版本,并进行多项测试（比如from和to为统一站点，time和fee输入不合法）
 * ****************************************************************
*/
//新建立路线
{
    qDebug() << "creating a Way: " <<from<<to<<time<<fee;

    QSqlDatabase db = SqlConnectionPool::openConnection();

    //始发站和终点站不能为同一站点，始发站和终点站必须为已有站点之一
    if(from==to){
        return("始发站和终点站不能为同一站点");
    }else {

        bool haveFromSite=false;
        bool haveToSite=false;
        QSqlQuery result = db.exec(" select siteId from StationTable");
        QString tmpSiteId;

        while(result.next()){
            tmpSiteId=result.value("siteId").toString();

            if(from==tmpSiteId){
                haveFromSite=true;
            }else if(to==tmpSiteId){
                haveToSite=true;
            }

        }

        if(!(haveToSite&&haveFromSite)){
            return("始发站和终点站必须为已有站点");
        }

    }

    double timeDouble = time.toDouble();
    double feeDouble = fee.toDouble();

    if(timeDouble<=0){
        return("时间耗费应该为正数");
        SqlConnectionPool::closeConnection(db);
    }

    if(feeDouble<=0){
        SqlConnectionPool::closeConnection(db);
        return("费用代价应该为正数");

    }

    /*
        * 上传time和fee 到 from 节点到 to 节点
        * 如果失败则返回错误原因的QString
        */
    QSqlQuery qsQuery = QSqlQuery(db);
    QString strSqlText("insert into LineTable (siteId,toSite,timeCost,fee)\
                       values (:siteId,:toSite,:timeCost,:fee)");
                       qsQuery.prepare(strSqlText);
            qsQuery.bindValue(":siteId",from);
    qsQuery.bindValue(":toSite",to);
    qsQuery.bindValue(":timeCost",time);
    qsQuery.bindValue(":fee",fee);
    bool result_successed =qsQuery.exec();
    if(result_successed){

        qDebug()<<"插入新路线信息成功";

    }else{

        qDebug()<<"插入新路线信息失败！";
        qDebug()<<"error open database because"<<db.lastError().text();
        return("向数据库插入信息失败");

    }

    SqlConnectionPool::closeConnection(db);


    return("Success");
}

const QString function::delWay(QString from, QString to)
/****************************************************************
 * Function:       function::delWay
 * Description:         删除路线
 * Calls(被本函数调用的函数):
 * Table Accessed(被访问的数据库中的表):StationTable LineTable
 * Table Updated(被修改的数据库中的表):LineTable
 * Input:      QString from   始发站, QString to  终点站
 * Output:    调试信息
 * Return:   表示删除路线成功与否的字符串
 * Others(其他说明，比如测试建议):
 *History(历史修改记录)：
 * <作者>   <时间>  <改动>
*刘英杰    2021/3     完成1.0版本
*李春毅    2021/4/18  修改返回错误，不存在的路线会返回“路线不存在”
 * ****************************************************************
*/
//删除路线
{
    qDebug() << "deleting a Way: " <<from<<to;
    QSqlDatabase db = SqlConnectionPool::openConnection();


    //查找指定路线是否存在

    bool haveThisLine=false;

    QSqlQuery result = db.exec(" select siteId, toSite from LineTable");
    QString valueSiteId;
    QString valueToSite;

    while(result.next()){
        valueSiteId=result.value("siteId").toString();
        valueToSite=result.value("toSite").toString();

        if(from==valueSiteId){
            if(to==valueToSite){
                haveThisLine=true;
            }

        }

    }

    qDebug()<<"OK?"<<haveThisLine;


    //进行删除

    if(haveThisLine){
        QSqlQuery qsQuery = QSqlQuery(db);
        QString strSqlText("DELETE FROM LineTable WHERE siteId = :siteId AND toSite=:toSite ");
        qsQuery.prepare(strSqlText);
        qsQuery.bindValue(":siteId", from);
        qsQuery.bindValue(":toSite", to);
        bool result_successed =qsQuery.exec();
        SqlConnectionPool::closeConnection(db);
        if(result_successed){

            qDebug()<<"删除路线信息成功";

        }else{

            qDebug()<<"删除路线信息失败！";
            qDebug()<<"error open database because"<<db.lastError().text();
            return("从数据库删除路线信息失败");

        }
    }else
        return ("该路线不存在");

    return("Success");
}

const QVector<QStringList> function::getAllGoodInfo()
//得到所有的运单档案信息，没有限制条件
/*
* Function:                             function::getAllGoodInfo
* Description:                          得到所有的运单档案信息
* Calls(被本函数调用的函数):               无
* Table Accessed(被访问的数据库中的表):     GoodsTable
* Table Updated(被修改的数据库中的表):
* Input:
* Output:                               所有未归档的运单信息
* Return:                               成功则返回运单列表
*                                       失败则无返回
* Others(其他说明，比如测试建议):
*
* History(历史修改记录)：
* <作者>      <时间>         <改动>
* 李春毅     2021/3/25      完成1.0版本，成功实现所需功能
*/
{
    qDebug() << "gettingAllGoodInfo...";


    QStringList goodsRow;  //一个运单里的一行信息
    QVector<QStringList> goodsTable; //所有运单


    QSqlDatabase db = SqlConnectionPool::openConnection();
    /*
         *
         * 获取运单表里运单数量赋值给goodsNum
         * 然后获取运单信息循环赋值给 goodsRow[]
         *
         */

    QSqlQuery result = db.exec(" select * from GoodsTable");

    SqlConnectionPool::closeConnection(db);
    qDebug()<<"will enter in the while";
    int i = 0;//实际运单数量/循环变量

    while(result.next()){
        if( result.value("state").toInt() == 5) // state 是 5 时表示已归档，无需显示
            continue;
        goodsRow << result.value("goodsId").toString();

        goodsRow << result.value("userId").toString();

        goodsRow << result.value("dispatchPlace").toString();

        goodsRow << result.value("receivePlace").toString();

        goodsRow << result.value("consignerName").toString();

        goodsRow << result.value("consigneeName").toString();

        goodsRow << result.value("consignerTele").toString();

        goodsRow << result.value("consigneeTele").toString();

        goodsRow << result.value("trackingNum").toString();

        goodsRow << result.value("currentPlace").toString();

        goodsRow << result.value("dispatchTime").toString();

        switch(result.value("state").toInt()){
        case 1: goodsRow << "良好";  break;
        case 2: goodsRow << "损坏";  break;
        case 3: goodsRow << "丢失";   break;
        case 4: goodsRow << "已签收";  break;
        }

        goodsTable << goodsRow;  //信息汇总

        goodsRow.clear();
    }


    return goodsTable;

}

const QVector<QStringList> function::getAccurateGoodInfo(int type, QString keyWords)
//档案查询页，根据某项属性查询关键词，得到准确信息
/*
* Function:                             function::getAllGoodInfo
* Description:                          得到所有的运单档案信息
* Calls(被本函数调用的函数):               无
* Table Accessed(被访问的数据库中的表):     GoodsTable
* Table Updated(被修改的数据库中的表):
* Input:                                type对应搜索关键词时的11种属性：
*                                       0货物编号、1用户编号、2发货地、3收货地、4发货人姓名、5收货人姓名、
*                                       6发货人电话号、7收货人电话号、8运输单号、9货物当前位置、10发货时间、11备注
*                                       keywords对应输入的关键词
*
* Output:                               符合条件的运单信息
* Return:                               成功则返回符合条件的运单列表
*                                       失败则无返回
* Others(其他说明，比如测试建议):
*
*
* History(历史修改记录)：
* <作者>      <时间>         <改动>
* 李春毅     2021/3/25      完成1.0版本，成功实现所需功能
*/
{

    //下为正文

    QString typeFind;                //类型

    //注意，type对应搜索关键词时的11种属性
    //它们分别是：0货物编号、1用户编号、2发货地、3收货地、4发货人姓名、5收货人姓名、
    //    6发货人电话号、7收货人电话号、8运输单号、9货物当前位置、10发货时间、11备注
    switch(type){
    case 0: {
        typeFind = "goodsId";
        break;
    }
    case 1: {
        typeFind = "userId";
        break;
    }
    case 2: {
        typeFind = "dispatchPlace";
        break;
    }
    case 3: {
        typeFind = "receivePlace";
        break;
    }
    case 4: {
        typeFind = "consignerName";
        break;
    }
    case 5: {
        typeFind = "consigneeName";
        break;
    }
    case 6: {
        typeFind = "consignerTele";
        break;
    }
    case 7: {
        typeFind = "consigneeTele";
        break;
    }
    case 8: {
        typeFind = "dtrackingNum";
        break;
    }
    case 9: {
        typeFind = "currentPlace";
        break;
    }
    case 10: {
        typeFind = "dispatchTime";
        break;
    }
    case 11: {
        typeFind = "state";
        break;
    }
    }

    QStringList goodsRow;  //一个运单里的一行信息
    QVector<QStringList> goodsTable;
    //所有运单


    //*****************************数据库连接**********************************
    QSqlDatabase db = SqlConnectionPool::openConnection();
    //****************************数据库连接***********************************


    QSqlQuery result = db.exec(" select * from GoodsTable where " + typeFind  + "='" + keyWords + "'" );
    qDebug() << result.result();

    qDebug()<<"will enter in the while";

    while(result.next()){
        if( result.value("state").toInt() == 5) // state 是 5 时表示已归档，无需显示
            continue;
        goodsRow << result.value("goodsId").toString();

        goodsRow << result.value("userId").toString();

        goodsRow << result.value("dispatchPlace").toString();

        goodsRow << result.value("receivePlace").toString();

        goodsRow << result.value("consignerName").toString();

        goodsRow << result.value("consigneeName").toString();

        goodsRow << result.value("consignerTele").toString();

        goodsRow << result.value("consigneeTele").toString();

        goodsRow << result.value("trackingNum").toString();

        goodsRow << result.value("currentPlace").toString();

        goodsRow << result.value("dispatchTime").toString();

        switch(result.value("state").toInt()){
        case 1: goodsRow << "良好";  break;
        case 2: goodsRow << "损坏";  break;
        case 3: goodsRow << "丢失";  break;
        case 4: goodsRow << "已签收"; break;
        }

        goodsTable << goodsRow;  //信息汇总

        goodsRow.clear();
    }
    SqlConnectionPool::closeConnection(db);
    return goodsTable;
}

const QVector<QStringList> function::getSiteGoodInfo(QString site, int ststus, QString keyWords)
//站点信息页，得到一个指定站点的运单信息，不包括已归档的信息（即note属性中不能有onFile关键词）
/*
* Function:                             function::getAllGoodInfo
* Description:                          获得站点的运单信息
* Calls(被本函数调用的函数):               无
* Table Accessed(被访问的数据库中的表):     GoodsTable
* Table Updated(被修改的数据库中的表):
* Input:                                site 站点名称
*                                       ststus 状态
*                                       keywords 要查询的关键词
*
* Output:                               符合条件的运单信息
* Return:                               成功则返回符合条件的运单列表
*                                       失败则无返回
*
* Others(其他说明，比如测试建议):           函数参数列表 缺乏type类型定义，无法keywords确定要查哪个属性
*                                       前端和数据库状态信息不匹配（已做调整，仍有冗余信息）
*
*
*
* History(历史修改记录)：
* <作者>      <时间>         <改动>
* 李春毅     2021/3/30      完成1.0版本，成功实现除查询以外所需功能
* 李春毅     2021/4/18      增加全局搜索功能
*/
//站点信息页，得到一个指定站点的运单信息，不包括已归档的信息（即note属性中不能有onFile关键词）
{
    //注意，keyword来自于搜索栏，在每次刷新站点之后搜索栏会清空，那么此值可能缺省
    //缺省了keyword就代表只根据站点和货物状态信息进行查询
    //状态信息来源于下拉框的标准状态信息，我猜想状态应该存储在数据库note一栏中
    //数据库中note中的内容可以比标准状态更多，需要的话，标准状态可以在前端增加
    //前端现有的标准状态（ststus）设置了六个：（需要的话，还可以以后版本添加更多）
    // 0      1      2      3      4     5     6
    //"全部" "截留" "良好" "待取" "已签收" "破损" "良好";

    //1:良好  2：损坏  3：丢失  4：已签收 5:已归档

    QSqlDatabase db = SqlConnectionPool::openConnection();
    SqlConnectionPool::closeConnection(db);
    //site和status是不会缺省的
    qDebug() << "gettingSiteGoodInfo:"<<site<<ststus<<keyWords;
    //...
    //an example of using QVector<QStringList>
    //******************************数据库连接*********************************

    //****************************数据库连接***********************************
    QStringList goodsRow;  //一个运单里的一行信息
    QVector<QStringList> goodsTable; //所有运单


    QString   trackingNum;
    QString   currentPlace;
    QSqlQuery result;

    QString findString = "";
    if(!keyWords.isEmpty()){
        findString ="where '" + keyWords + "' in(goodsId,userId,dispatchPlace,receivePlace,"
                                           "consignerName, consigneeName , consignerTele , "
                                           "consigneeTele , trackingNum ,currentPlace ,dispatchTime ,state)";
        qDebug() << "findString is ＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊"+ findString;

        result = db.exec(QString("select * from GoodsTable " + findString));
    }
    else{
        result = db.exec(QString("select * from GoodsTable"));
    }
    int i = 0;  //货物表里的货物个数
    int findValue = 0;
    switch(ststus){
    case 4:
        findValue = 4;
        break;
    case 1:
        findValue = 3;
        break;
    case 5:
        findValue = 2;
        break;
    case 2:
    case 3:
    case 6:
        findValue = 1;
        break;
    }

    while(result.next()){
        //查所有的运单信息，保存字符串里有含有site字符的运输单号，和当前位置
        trackingNum = result.value("trackingNum").toString();  //找到运单号

        if(trackingNum.contains(site)){

            currentPlace = result.value("currentPlace").toString();    //找到当前位置

            if(trackingNum.indexOf(currentPlace) >= trackingNum.indexOf(site)){
                // 如果当前位置的字符在运输单号里site字符相同位置或者其后，就显示该货物的详细信息
                //   ststus=0,      对应state != 5
                //   ststus=2,3,6   对应state = 1
                //   ststus=5,      对应state = 2
                //   ststus=1       对应state = 3
                //   ststus=4,      对应state = 4
                //然后用keyword限制，keyword为空则直接显示，如果keyword不为空按照type类型查找即可

                goodsRow<< result.value("goodsId").toString();

                goodsRow << result.value("userId").toString();

                goodsRow << result.value("dispatchPlace").toString();

                goodsRow << result.value("receivePlace").toString();

                goodsRow << result.value("consignerName").toString();

                goodsRow << result.value("consigneeName").toString();

                goodsRow << result.value("consignerTele").toString();

                goodsRow << result.value("consigneeTele").toString();

                goodsRow << result.value("trackingNum").toString();

                goodsRow << result.value("currentPlace").toString();

                goodsRow << result.value("dispatchTime").toString();

                switch(result.value("state").toInt()){
                case 1: goodsRow << "良好";  break;
                case 2: goodsRow << "损坏";  break;
                case 3: goodsRow << "丢失";  break;
                case 4: goodsRow << "已签收"; break;
                }
                if(result.value("state").toInt() != 5 && (findValue == result.value("state").toInt() || findValue == 0))
                    goodsTable << goodsRow;  //信息汇总
                goodsRow.clear();

            }
        }
    }
    SqlConnectionPool::closeConnection(db);
    return goodsTable;
}


const QString function::addGoodInfo(QString goodsId, QString userId, QString dispatchPlace,
                                    QString receivePlace, QString consignerName,
                                    QString consigneeName, QString consignerTele,
                                    QString consigneeTele, QString trackingNum,
                                    QString currentPlace, QString dispatchTime, QString note)
//站点信息页，添加货物信息，参数与数据库12个属性同名
/*
* Function:                             function::addGoodInfo
* Description:                          添加货物信息
* Calls(被本函数调用的函数):               无
* Table Accessed(被访问的数据库中的表):     GoodsTable
* Table Updated(被修改的数据库中的表):      GoodsTable
* Input:                                所有运单属性
*
* Output:                               数据库返回插入结果
* Return:                               成功则返回Success
*                                       失败则返回数据库原因
*
* Others(其他说明，比如测试建议):           无法判断输入是否有错，只能在数据库端做限制，失败则重新插入
*
*
* History(历史修改记录)：
* <作者>      <时间>         <改动>
* 李春毅     2021/3/25      完成1.0版本，实现插入功能
* 李春毅     2021/4/18      对输入字符做了限制，确保上传到数据库的数据格式的正确
* 		        对输入状态信息做了数符转换
*/
{
    qDebug() << "增加货物信息："<<goodsId;

    QString errReason = "";

    /*
         * 给表里新增一条记录
         * 如果失败则用qDebug()打印原因、并把原因赋值给 errReason;
         *
         */

    //******************************数据库连接*********************************
    QSqlDatabase db = SqlConnectionPool::openConnection();

    //****************************数据库连接***********************************

    if(        goodsId.isEmpty()         || userId.isEmpty()
               || dispatchPlace.isEmpty()   || receivePlace.isEmpty()   || consignerName.isEmpty()
               || consigneeName.isEmpty()   || consignerTele.isEmpty()
               || consigneeTele.isEmpty()   || trackingNum.isEmpty()
               || currentPlace.isEmpty()    || dispatchTime.isEmpty()   || note.isEmpty()){

        errReason = "输入信息不能为空";
        return errReason;
    }
    if(goodsId.length() > 20){
        errReason = "货物编号长度超过20位";
        return errReason;
    }
    if(userId.length() > 20){
        errReason = "用户编号长度超过20位";
        return errReason;
    }
    if(dispatchPlace.length() > 10){
        errReason = "发货地字符长度超过10位";
        return errReason;
    }
    if(receivePlace.length() > 10){
        errReason = "发货地字符长度超过10位";
        return errReason;
    }
    if(consignerName.length() > 10){
        errReason = "发货人姓名长度超过10位";
        return errReason;
    }
    if(consigneeName.length() > 10){
        errReason = "收货人姓名长度超过10位";
        return errReason;
    }
    if(consignerTele.length() != 11){
        errReason = "发货人电话号长度有误";
        return errReason;
    }
    if(consigneeTele.length() != 11){
        errReason = "收货人电话号长度有误";
        return errReason;
    }
    if(trackingNum.length() > 100){
        errReason = "运输单号长度超过100位";
        return errReason;
    }
    if(currentPlace.length() > 10){
        errReason = "当前位置字符长度超过10位";
        return errReason;
    }
    if(dispatchTime.length() > 20){
        errReason = "发货时间字符长度超过20位";
        return errReason;
    }
    QString noteToState; //数符转换
    if(note == "良好"){
        noteToState = "1";
    }else if(note == "损坏"){
        noteToState = "2";
    }else if(note == "丢失"){
        noteToState = "3";
    }else if(note == "已签收"){
        noteToState = "4";
    }else{
        errReason = "输入备注有误。以下是合法备注：“良好”、“损坏”、“丢失”、“已签收” ";
        return errReason;
    }

    QSqlQuery query(db);

    query.prepare(QString("insert into GoodsTable(goodsId,userId,dispatchPlace,"
                          "receivePlace,consignerName, consigneeName , consignerTele ,"
                          " consigneeTele , trackingNum ,currentPlace ,dispatchTime ,state)"
                          "values('"+ goodsId +"','"+ userId +"','"+ dispatchPlace +"','"
                          + receivePlace +"','"+ consignerName	+"','"+ consigneeName +"','"
                          + consignerTele +"','"+ consigneeTele +"','"+ trackingNum +"','"
                          + currentPlace +"','"+ dispatchTime +"','"+ noteToState +"')"));


    bool ans = query.exec();
    if(ans){
        qDebug()<< "新增成功";
    }
    else{
        qDebug()<< "数据库错误";
        errReason = "请检查货物编号是否存在，或检查发货地或收货地是否存在";

    }
    SqlConnectionPool::closeConnection(db);
    if(errReason == "")
        return("Success");
    return(errReason);
}

const QString function::editGoodInfo(QString goodsId, QString userId, QString dispatchPlace,
                                     QString receivePlace, QString consignerName,
                                     QString consigneeName, QString consignerTele,
                                     QString consigneeTele, QString trackingNum,
                                     QString currentPlace, QString dispatchTime, QString note)
//站点信息页，更改货物信息，参数与数据库12个属性同名
/*
* Function:                             function::editGoodInfo
* Description:                          修改运单信息
* Calls(被本函数调用的函数):               无
* Table Accessed(被访问的数据库中的表):     GoodsTable
* Table Updated(被修改的数据库中的表):      GoodsTable
* Input:                                所有运单属性
*
* Output:                               数据库返回修改结果
* Return:                               成功则返回Success
*                                       失败则返回数据库原因
*
* Others(其他说明，比如测试建议):           无法判断输入是否有错，只能在数据库端做限制，失败则重新插入
*
*
* History(历史修改记录)：
* <作者>      <时间>         <改动>
* 李春毅     2021/3/25      完成1.0版本，update语句无法实现
* 李春毅     2021/3/30      完成2.0版本，update成功更新，实现更改货物信息的功能
* 李春毅     2021/4/18      对输入字符做了限制，确保上传到数据库的数据格式的正确
*                          如果修改了数据库中不存在goodsId，则接下来的信息都作为新的货物信息上传，存在则修改除了主键的其它属性
*/
{
    qDebug() << "editingGoodInfo..."<<goodsId;

    QString errReason = "";

    /*
         * 覆盖goodsId的所有信息
         * 如果失败则用qDebug()打印原因、并把原因赋值给 errReason;
         *
         */

    if(consigneeTele.length()!=11 || consignerTele.length()!=11){
        errReason = "寄件人或收件人电话号码长度错误";
        return errReason;
    }
    //******************************数据库连接*********************************
    QSqlDatabase db = SqlConnectionPool::openConnection();
    //****************************数据库连接***********************************


    if(        goodsId.isEmpty()        || userId.isEmpty()
               || dispatchPlace.isEmpty()   || receivePlace.isEmpty()   || consignerName.isEmpty()
               || consigneeName.isEmpty()   || consignerTele.isEmpty()
               || consigneeTele.isEmpty()   || trackingNum.isEmpty()
               || currentPlace.isEmpty()    || dispatchTime.isEmpty()   || note.isEmpty()){

        errReason = "输入信息不能为空";
        return errReason;
    }
    if(goodsId.length() > 20){
        errReason = "货物编号长度超过20位";
        return errReason;
    }
    if(userId.length() > 20){
        errReason = "用户编号长度超过20位";
        return errReason;
    }
    if(dispatchPlace.length() > 10){
        errReason = "发货地字符长度超过10位";
        return errReason;
    }
    if(receivePlace.length() > 10){
        errReason = "发货地字符长度超过10位";
        return errReason;
    }
    if(consignerName.length() > 10){
        errReason = "发货人姓名长度超过10位";
        return errReason;
    }
    if(consigneeName.length() > 10){
        errReason = "收货人姓名长度超过10位";
        return errReason;
    }
    if(consignerTele.length() != 11){
        errReason = "发货人电话号长度有误";
        return errReason;
    }
    if(consigneeTele.length() != 11){
        errReason = "收货人电话号长度有误";
        return errReason;
    }
    if(trackingNum.length() > 100){
        errReason = "运输单号长度超过100位";
        return errReason;
    }
    if(currentPlace.length() > 10){
        errReason = "当前位置字符长度超过10位";
        return errReason;
    }
    if(dispatchTime.length() > 20){
        errReason = "发货时间字符长度超过20位";
        return errReason;
    }
    QString noteToState; //数符转换
    if(note == "良好"){
        noteToState = "1";
    }else if(note == "损坏"){
        noteToState = "2";
    }else if(note == "丢失"){
        noteToState = "3";
    }else if(note == "已签收"){
        noteToState = "4";
    }else{
        errReason = "输入备注有误。以下是合法备注：“良好”、“损坏”、“丢失”、“已签收” ";
        return errReason;
    }

    QSqlQuery queryFind(db);

    queryFind.prepare(QString("SELECT * FROM GoodsTable WHERE goodsId ='" + goodsId + "'"));

    bool ans1 = queryFind.exec();
    if(ans1){
        qDebug()<< "查询成功";
    }
    else{
        qDebug()<< "无此货物Id";
        QString res = addGoodInfo(goodsId, userId, dispatchPlace,receivePlace,  consignerName,
                                  consigneeName,  consignerTele,
                                  consigneeTele,  trackingNum,
                                  currentPlace,  dispatchTime,  note);
        return res;
    }

    QSqlQuery query(db);

    query.prepare(QString("UPDATE  GoodsTable  SET  userId='" + userId
                          + "', dispatchPlace='" + dispatchPlace + "', receivePlace='" + receivePlace
                          + "', consignerName='" + consignerName + "', consigneeName='" +  consigneeName
                          + "', consignerTele='" + consignerTele + "', consigneeTele='" + consigneeTele
                          + "', trackingNum='" + trackingNum + "', currentPlace='" + currentPlace
                          + "', dispatchTime='" + dispatchTime + "', state='"+ noteToState+"' WHERE goodsId ='" + goodsId + "'"));

    bool ans = query.exec();
    if(ans){
        qDebug()<< "修改成功";
    }
    else{
        qDebug()<< "外键约束错误";
        errReason = "请检查发货地或收货地是否存在";

    }
    SqlConnectionPool::closeConnection(db);
    if(errReason == "")
        return("Success");
    return(errReason);
}

const QString function::delGoodInfo(QString goodsId)
//站点信息页，归档货物信息
/*
* Function:                             function::delGoodInfo
* Description:                          运单归档
* Calls(被本函数调用的函数):               无
* Table Accessed(被访问的数据库中的表):     GoodsTable
* Table Updated(被修改的数据库中的表):      GoodsTable
* Input:                                goodsId 货物id
*
* Output:                               数据库返回修改结果
* Return:                               成功则返回Success
*                                       失败则返回数据库原因
*
* Others(其他说明，比如测试建议):           无
*
*
* History(历史修改记录)：
* <作者>      <时间>         <改动>
* 李春毅     2021/3/25      完成1.0版本，update语句无法实现
* 李春毅     2021/3/30      完成2.0版本，update成功更新，实现运单归档的功能
*/
{
    //说明：我写到这里发现不得不思考一些后端逻辑的问题
    //站点内信息管理的初衷在于减少站点管理员负荷，方便管理，而不在所有信息中大海捞针
    //而实际上已经送到客户手中的运单在某些情况下仍然需要被站点管理员查询
    //所以我臆造了一个还没有讨论过的运单标准状态（note中的关键字）：onFile（归档）
    //这种情况下，“删除信息”按钮的作用就变成了“把不需要查到的信息进行归档”
    //即后端需要在note中添加“onFile”关键字
    //而在function::getSiteGoodInfo()中，请务必过滤掉“onFile”关键字信息，以保证归档信息隐藏

    qDebug() << "deletingGoodInfo..."<<goodsId;
    //注意：只根据goodsId进行查找与归档
    QString errReason = "";

    /*
        *
        * 将该goodsId的note属性值设置为5
        * 如果失败则用qDebug()打印原因、并把原因赋值给 errReason;
        *
        */
    //******************************数据库连接*********************************
    QSqlDatabase db = SqlConnectionPool::openConnection();

    //****************************数据库连接***********************************



    QSqlQuery query(db);

    bool res = query.exec(QString("UPDATE  GoodsTable  SET   state=5   where  goodsId=" + goodsId));

    if(res){
        qDebug() << "修改state成功";
    }
    else{
        qDebug() << "修改state失败";
        errReason = "数据库错误";
    }
     SqlConnectionPool::closeConnection(db);
    if(errReason == "")
        return("Success");
    return(errReason);
}

const QVector<QStringList> function::mailPushing(QString from, QString to)
/****************************************************************
 * Function:        function::mailPushing
 * Description:         根据起点终点为用户选择三种路线方案
 * Calls(被本函数调用的函数):        find（）
 * Table Accessed(被访问的数据库中的表):  无
 * Table Updated(被修改的数据库中的表):   无
 * Input:      from ： 起始站点  to ：目的站点
 * Output:    测试信息
 * Return:   登录成功与否的QString
 * Others(其他说明，比如测试建议):   测试各种站点选择；
 *History(历史修改记录)：
 * <作者>   <时间>  <改动>
 * 金雨展     2021/3/20      完成1.0版本，并验证了一些测试样例
 * 金雨展      2021/3/22   完成2.0版本
 *                                      修改BUG：
 *
 * ****************************************************************
*/
//给起点终点，推送三个路线方案
{
    RoutePlanning rp;
    qDebug() << "mailPushing:"<<from<<to;

    QList<QString> project1 = rp.find(from,to,1);//timecost first
    QString timecost1 = project1[0];//时间
    QString timecost2 = project1[1];//费用
    timepath = project1[2];//运输单号

    QList<QString> project2 = rp.find(from,to,2);//feecost first
    QString feecost1 = project2[0];
    QString feecost2 = project2[1];
    feepath = project2[2];//运输单号


    QList<QString> project3 = rp.find(from,to,3);//性价比 first
    QString cost1 = project3[0];
    QString cost2 = project3[1];
    path = project3[2];//运输单号

    //...
    //an example of using QVector<QStringList>
    QVector<QStringList> example;
    QStringList row1,row2,row3;
    //这里的第三个项，主要是为了方便前端能够在LED里面显示价格，不在表格中显示
    //qDebug() <<cost1<<"   "<<cost2;

    row1<<"时间最短方案"<<"时间：" + timecost1 + " h   费用： "  + timecost2 + " 元"<< timecost1;
    row2<<"花费最少方案"<<"时间：" + feecost1 + " h   费用： "  + feecost2 + " 元"<< feecost1;
    row3<<"性价比最高方案"<<"时间：" + cost1 + " h   费用： "  + cost2 + " 元"<< cost1;
    example<<row1<<row2<<row3;
    return example;
}
QString getRandomString(int length)
{
    qsrand(QDateTime::currentMSecsSinceEpoch());//为随机值设定一个seed

    const char chrs[] = "0123456789";
    int chrs_size = sizeof(chrs);

    char* ch = new char[length + 1];
    memset(ch, 0, length + 1);
    int randomx = 0;
    for (int i = 0; i < length; ++i)
    {
        randomx= rand() % (chrs_size - 1);
        ch[i] = chrs[randomx];
    }

    QString ret(ch);
    delete[] ch;
    return ret;
}


const QString function::pay(QString dispatchPlace, QString receivePlace,
                             QString userId,
                             QString consignerName, QString consignerTele,
                             QString consigneeName, QString consigneeTele,
                             int pushChoosing, double cost)
/****************************************************************
 * Function:        function::pay
 * Description:         寄件计价页面的信息录入
 * Calls(被本函数调用的函数):        无
 * Table Accessed(被访问的数据库中的表):  GoodsTable
 * Table Updated(被修改的数据库中的表):   GoodsTable
 * Input:     dispatchPlace ：发货地   receivePlace ：收货地
 *            userId ：用户ID
 *            consignerName ： 寄件人姓名  consignerTele ：收件人姓名
 *            consigneeName ： 寄件人电话  consigneeTele ：收件人电话
 *            pushChoosing ：用户选择的路线方案
 * Output:    测试信息
 * Return:   登录成功与否的QString
 * 测试：  寄件人收件人电话号码规范
 * Others(其他说明，比如测试建议):   测试各种方案选择；
 *History(历史修改记录)：
 * <作者>   <时间>  <改动>
 * 金雨展     2021/3.30       完成1.0版本，并验证了一些测试样例
 * 金雨展      2021/4/3   增加条件判断，并进行改建
 *            2021/4/7             修改BUG：
 *            2021/4/18            修改BUG
 * 李春毅      2021/4/20            修改寄件人姓名长度2-20(原3-20）
 * ****************************************************************
*/

//选好了一个路线方案之后，客户付钱，程序增加一条货物信息
{
    //注意，这里的前7个参数与数据库货物信息表同名，剩下没写的参数应当是这里的程序生成的
    //注意，pushChoosing是三种推荐的选择结果，值为0、1、2，对应funtion::mailPushing()的三项推荐方案
    //cost是支付数字

    //判断货物信息是否合法
    if(consignerTele.length() != 11){
        qDebug()<<"寄件人电话号码不正确";
        return(" 请输入正确的寄件人电话号码！");

    }
    else if(consigneeTele.length() != 11){
        qDebug()<<"收件人电话号码不正确";
        return(" 请输入正确的收件人电话号码！");
    }

    //判断寄件人姓名是否合法
    if(consignerName.length()<=20 && consignerName.length()>=1){

    }
    else{
        qDebug()<<"寄件人姓名不正确";
        return(" 请输入正确的寄件人姓名！");
    }

    //判断收件人姓名是否合法
    if(consigneeName.length()<=20 && consigneeName.length()>=1){

    }
    else{
        qDebug()<<"收件人姓名不正确";
        return(" 请输入正确的收件人姓名！");
    }



    //***************************数据库***************************
    QSqlDatabase db = SqlConnectionPool::openConnection();

    //***********************************************************

    //判断用户ID是否合法
    if(userId.length()<=20 && userId.length()>=1){

    }
    else{
        qDebug()<<"用户账号长度不合适不正确";
        return(" 请输入正确的用户账号！");
    }





    bool ifuse = false;
    QString rowname = "userId";
    QString tablename = "UserTable";
    QSqlQuery result = db.exec(" select " + rowname + " from " + tablename + ";");
    while(result.next()){
        qDebug()<<"userId:"<<result.value("userId");

        if(result.value("userId").toString() ==  userId){

            qDebug()<<"找到该账号";
            ifuse = true;

            break;
        }

    }
    if(ifuse == false){
        qDebug()<<"找到相应用户账号";
        return(" 该用户账号不存在");
    }
    //运单号选择********************
    QString trackingNum;
    if(pushChoosing == 1)
        trackingNum = timepath;
    else if(pushChoosing == 2)
        trackingNum = feepath;
    else if(pushChoosing == 3)
        trackingNum = path;
    if(trackingNum.length() <= 0){
        return "请生成推荐路线方案并选择一条路线";
    }
    QString trknm;
    //由于生成的运输单号是是从右往左的，因此需要翻转一下方向
         for(auto i =  trackingNum.rbegin(); i < trackingNum.rend(); i++)
            trknm += * i;

    trackingNum = trknm;

    QString errReason = "";
    //随机生成货物编号
    //检测改货物单号是否合理
    qDebug()<<"******************读货物编号开始****************";
    bool use = false;

    QSqlQuery res(db);

    QString goodsId;

    do{
        goodsId = getRandomString(4);

        qDebug() << "生成货物编号："<<goodsId;

        res.prepare(QString("select goodsId from GoodsTable where goodsId= :goodsId;"));

        res.bindValue(":goodsId", goodsId);

        res.exec();
        res.next();

        if(res.value(0).toString() ==  goodsId){
           qDebug()<<"该货物编号已使用";
           use = true;
        }
        res.clear();

    }while(use);



    QDateTime dTime;
    QTime time;
    QDate date;
    dTime.setTime(time.currentTime());
    dTime.setDate(date.currentDate());
    QString dispatchTime = dTime.toString("yyyy:MM:dd:hh:mm:ss");

    qDebug() << dispatchTime;

    QString note = "1";
    QSqlQuery query(db);

    query.prepare(QString("insert into GoodsTable(goodsId,userId,dispatchPlace,receivePlace,consignerName, consigneeName , consignerTele , consigneeTele , trackingNum ,currentPlace ,dispatchTime ,state)"
                          "values('"+ goodsId +"','"+ userId +"','"+ dispatchPlace +"','"+ receivePlace +"','"+ consignerName	+"','"+ consigneeName +"','"+ consignerTele +"','"+ consigneeTele +"','"+ trackingNum +"','"+ dispatchPlace +"','"+ dispatchTime +"','"+ note +"')"));


    bool ans = query.exec();
    if(ans){
        qDebug()<< "新增成功";
    }
    else{
        qDebug()<< "新增失败";
        errReason = "新增失败";
    }
    SqlConnectionPool::closeConnection(db);
    if(errReason == "")
        return("货物寄出成功！");
    return(errReason);
}
const wayInfo function::getWay()
//获取路线信息
//wayInfo结构体的各项含义：
//QList<double> point1: 一条路线的第一个点的x,y坐标，两个两个读取
//QList<double> point2: 一条路线的第二个点的x,y坐标，两个两个读取
//QList<QString> name:  一条路线的第一个点的名称和第二个点的名称，两个两个读取
//QList<double> weight: 一条路线的事件权重和费用权重，两个两个读取
{
    //格式示例
    QList<double> point1; //point1 << 1 << 2;
    QList<double> point2; //point2 << 3 << 4;
    QList<QString> name;  //name << "A" << "0";
    QList<double> weight; //weight << 10.5 << 30.6;

    QSqlDatabase db = SqlConnectionPool::openConnection();

    QString siteId, toSite;
    double x1, y1, x2, y2;
    double timeCost, fee;

    QSqlQuery resultLine = db.exec(QString("select * from LineTable;"));

    while(resultLine.next()){
        siteId = resultLine.value("siteId").toString();


        QSqlQuery resultPoint1(db);

        resultPoint1.prepare(QString("select coordinateX, coordinateY from StationTable where siteId = :siteId  ;"));
        resultPoint1.bindValue(":siteId", siteId);
        resultPoint1.exec();
        resultPoint1.next();
        x1 = resultPoint1.value(0).toDouble();
        y1 = resultPoint1.value(1).toDouble();

        resultPoint1.clear();

        point1 << x1 << y1;

        toSite = resultLine.value("toSite").toString();

        QSqlQuery resultPoint2(db);

        resultPoint2.prepare(QString("select coordinateX, coordinateY from StationTable where siteId = :toSite  ;"));
        resultPoint2.bindValue(":toSite", toSite);
        resultPoint2.exec();
        resultPoint2.next();

        x2 = resultPoint2.value(0).toDouble();
        y2 = resultPoint2.value(1).toDouble();

        resultPoint2.clear();

        point2 << x2 << y2;

        name << siteId << toSite;

        timeCost = resultLine.value("timeCost").toDouble();
        fee = resultLine.value("fee").toDouble();

        weight << timeCost << fee;

//        qDebug() << "";
//        qDebug() << "p1 x:" << x1 << ",  y:" << y1;
//        qDebug() << "p2 x:" << x2 << ",  y:" << y2;
//        qDebug() << "name1:" << siteId << ", name2:" << toSite;
//        qDebug() << "Weight: (timeCost)" << timeCost << ", (fee)" << fee;
//        qDebug() << "";

    }

    wayInfo info = {point1, point2, name, weight};

//    qDebug() << point1 ;
//    qDebug() << point2 ;
//    qDebug() << name ;
//    qDebug() << weight;

    SqlConnectionPool::closeConnection(db);

    return info;
}

